<cfcomponent displayname="FusecacheItem" hint="">

	<cfproperty name="name" type="string">
	<cfproperty name="foundInSettings" type="boolean">
	<cfproperty name="settings" type="fusecache.FuseSettings">
	<cfproperty name="allConditionsPassed" type="boolean">
	<cfproperty name="cacheEngine" type="any">
	<cfproperty name="foundInCache" type="boolean">
	<cfproperty name="cacheData" type="any">
	
	<cfset variables.instance = {} />
	
	<!--- init() --->
	<cffunction name="init" access="public" output="no" returntype="fusecache.FusecacheItem">
		<cfargument name="oFusecacheFactory" type="fusecache.FusecacheFactory" required="true" />
		<cfscript>
			setDefaults();
			setCacheEngine(arguments.oFusecacheFactory.getCacheEngine());
			variables.instance["oFusecacheFactory"] = arguments.oFusecacheFactory;
			return this;
		</cfscript>
	</cffunction>
	
	
	<!--- load() --->
	<cffunction name="load" access="public" output="no" returntype="fusecache.FusecacheItem">
		<cfargument name="oFusecacheFactory" type="fusecache.FusecacheFactory" required="true" />
		<cfargument name="oItemSettings" type="fusecache.FuseSettings" required="true" />
		<cfscript>
			init(arguments.oFusecacheFactory);
			populate(arguments.oItemSettings);
			return this;
		</cfscript>
	</cffunction>
	
	
	<!--- setDefaults() --->
	<cffunction name="setDefaults" access="private" output="no" returntype="void" hint="Set the defaults for this object.">
		<cfset this.name = "" />
		<cfset this.foundInSettings = false />
		<cfset this.settings = CreateObject("component","fusecache.FuseSettings").init() />
		<cfset this.allConditionsPassed = false />
		<cfset this.foundInCache = false />
		<cfset this.cacheData = "" />
	</cffunction>
	
	
	<!--- populate() --->
	<cffunction name="populate" access="private" output="no" returntype="void" hint="Accepts a struct and populates a single object." >
		<cfargument name="stFusecacheItemSettings" type="fusecache.FuseSettings" required="Yes" />
		<cfset var local = {} />
		<cfset this.name = arguments.stFusecacheItemSettings.name />
		<cfset this.foundInSettings = true />
		<cfset this.settings = arguments.stFusecacheItemSettings />
	</cffunction>
	
	
	<!--- Getters --->
	<!--- getName() --->
	<cffunction name="getName" access="public" output="false" returntype="string">
		<cfreturn this.name />
	</cffunction>
	
	
	<!--- getFoundInSettings() --->
	<cffunction name="getFoundInSettings" access="public" output="false" returntype="boolean">
		<cfreturn this["foundInSettings"] />
	</cffunction>
	
	
	<!--- getSettings() --->
	<cffunction name="getSettings" access="public" output="false" returntype="fusecache.FuseSettings">
		<cfreturn this["settings"] />
	</cffunction>
	
	
	<!--- getAllConditionsPassed() --->
	<cffunction name="getAllConditionsPassed" access="public" output="false" returntype="boolean">
		<cfreturn this["allConditionsPassed"] />
	</cffunction>
	
	
	<!--- getCacheEngine() --->
	<cffunction name="getCacheEngine" access="public" output="false" returntype="any">
		<cfreturn this["cacheEngine"] />
	</cffunction>
	
	
	<!--- getFoundInCache() --->
	<cffunction name="getFoundInCache" access="public" output="false" returntype="boolean">
		<cfreturn this["foundInCache"] />
	</cffunction>
	
	
	<!--- getCacheData() --->
	<cffunction name="getCacheData" access="public" output="false" returntype="any">
		<cfreturn this["cacheData"] />
	</cffunction>
	
	
	<!--- getConditions() --->
	<cffunction name="getConditions" access="public" output="false" returntype="array">
		<cfreturn getSettings().getConditions() />
	</cffunction>
	
	
	<!--- getVariables() --->
	<cffunction name="getVariables" access="public" output="false" returntype="array">
		<cfreturn getSettings().getVariables() />
	</cffunction>
	
	
	<!--- getType() --->
	<cffunction name="getType" access="public" output="false" returntype="string">
		<cfreturn getSettings().getType() />
	</cffunction>
	
	
	<!--- getCacheKey() --->
	<cffunction name="getCacheKey" access="public" output="false" returntype="string">
		<cfreturn getSettings().getKey() />
	</cffunction>
	
	
	<!--- getExpiration() --->
	<cffunction name="getExpiration" access="public" output="false" returntype="string">
		<cfreturn getSettings().getExpires() />
	</cffunction>
	
	
	<!--- getStaticFilePath() --->
	<cffunction name="getStaticFilePath" access="public" output="false" returntype="string">
		<cfreturn getSettings().getStaticFile() />
	</cffunction>
	
	
	<!--- getStaticFileRootPath() --->
	<cffunction name="getStaticFileRootPath" access="public" output="false" returntype="string">
		<cfreturn application.oFusecacheFactory.getStaticFileRootPath() />
	</cffunction>
	
	
	<!--- Setters --->
	<!--- setAllConditionsPassed() --->
	<cffunction name="setAllConditionsPassed" access="public" output="false" returntype="void" hint="Sets a boolean flag indicated whether this FusecacheItem has passed all of its related conditions.">
		<cfargument name="blnPassedCondition" type="boolean" required="true" />
		<cfset this.allConditionsPassed = arguments.blnPassedCondition />
	</cffunction>
	
	
	<!--- setName() --->
	<cffunction name="setName" access="public" output="false" returntype="void" hint="Sets the name property of this FusecacheItem">
		<cfargument name="sName" type="string" required="true" />
		<cfset this.name = arguments.sName />
	</cffunction>
	
	
	<!--- setCacheEngine() --->
	<cffunction name="setCacheEngine" access="public" output="false" returntype="void" hint="Receives and saves a reference to the cache engine object.">
		<cfargument name="cacheEngine" type="any" required="true" />
		<cfset this.cacheEngine = arguments.CacheEngine />
	</cffunction>
	
	
	<!--- setFoundInCache() --->
	<cffunction name="setFoundInCache" access="public" output="false" returntype="void" hint="Sets a boolean flag that indicated whether or not the FusecacheItem's data was found in the cache.">
		<cfargument name="blnFoundInCache" type="boolean" required="true" />
		<cfset this.foundInCache = arguments.blnFoundInCache />
	</cffunction>
	
	
	<!--- setCacheKey() --->
	<cffunction name="setCacheKey" access="public" output="false" returntype="void" hint="Sets the cache key into the FusecacheItem object.">
		<cfargument name="sCacheKey" type="string" required="true" />
		<cfset getSettings().setKey(arguments.sCacheKey) />
	</cffunction>
	
	
	<!--- setCacheData() --->
	<cffunction name="setCacheData" access="public" output="false" returntype="void" hint="Saves cache data into the FusecacheItem object (not the actual cache).">
		<cfargument name="cacheData" type="any" required="true" />
		<cfset this.cacheData = arguments.cacheData />
	</cffunction>
	
	
	<!--- hasConditions() --->
	<cffunction name="hasConditions" access="public" output="false" returntype="boolean" hint="Returns a boolean based on whether or not this FusecacheItem has a conditional block associated with it.">>
		<cftry>
			<cfif (ArrayLen(getConditions()))>
				<cfreturn true />
			</cfif>
			<cfcatch type="any">
				<cfreturn false />
			</cfcatch>
		</cftry>
		<cfreturn false />
	</cffunction>
	
	
	<!--- hasVariables() --->
	<cffunction name="hasVariables" access="public" output="false" returntype="boolean" hint="Returns a boolean based on whether or not this FusecacheItem has a variable block associated with it.">
		<cftry>
			<cfif (ArrayLen(getVariables()))>
				<cfreturn true />
			</cfif>
			<cfcatch type="any">
				<cfreturn false />
			</cfcatch>
		</cftry>
		<cfreturn false />
	</cffunction>
	
	
	<!--- testConditionsOrVariables() --->
	<cffunction name="testConditionsOrVariables" access="public" output="false" returntype="boolean" hint="Receives a struct containing the content of one or more variable scopes (Request, Session, etc).  It then attempts to validate the conditions via a try/catch evaluation.">
		<cfargument name="stVariablesScopes" type="struct" required="yes" />
		<cfscript>
			var local = {};
			local.arItemResults = ArrayNew(2);
			if (hasConditions()) { local.items = getConditions(); }
			else if (hasVariables()) { local.items = getVariables(); }
		</cfscript>
		
		<!--- Logic for conditions --->
		<cfif (hasConditions())>
			<!--- Loop over each condition --->
			<cfloop from="1" to="#ArrayLen(local.items)#" index="i">
				<cftry>
					
					<!--- Test the condition --->
					<cfset local.blnConditionTestResult = Evaluate("arguments.stVariablesScopes." & local.items[i][1]) />
					<cfif (NOT local.blnConditionTestResult)>
						<!--- If the evaluation proves false, set object state and stop looping --->
						<cfset setAllConditionsPassed(false) />
						<cfreturn false />
					</cfif>
					
					<!--- If any error happens, just count it out as a condition fail --->
					<cfcatch type="any">
						<cfset setAllConditionsPassed(false) />
						<cfreturn false />
					</cfcatch>
					
				</cftry>
			</cfloop>
		</cfif>
		
		<!--- Logic for variables --->
		<cfif (hasVariables())>
			<cftry>
				<cfset local.variableEvaluation = Evaluate("arguments.stVariablesScopes." & local.items[i][1]) />
				<cfset local.arVariableEvalResults = ArrayNew(1) />
				<cfset ArrayAppend(local.arVariableEvalResults,local.items[i][2]) />
				<cfset ArrayAppend(local.arVariableEvalResults,local.variableEvaluation) />
				<cfset ArrayAppend(local.arItemResults, local.arVariableEvalResults) />
				
				<!--- If we're dealing with variables and not conditions, then we need to build a dynamic cache key.
				We're passing it the result array from the previous loop which will provide the key-value pairs to
				customize the cache key --->
				<cfif (hasVariables())>
					<cfset buildDynamicKey(local.arItemResults) />
				</cfif>
				
				<cfcatch type="any">
					<cfset setAllConditionsPassed(false) />
					<cfreturn false />
				</cfcatch>
			</cftry>
		</cfif>

		<!--- If we've gotten far things have checked out. Get the data and set states. --->
		<cfset retrieveCacheData() />
		<cfset setAllConditionsPassed(true) />
		<cfreturn true />
		
	</cffunction>
	
	
	<!--- getScopesFromConditionsOrVariables() --->
	<cffunction name="getScopesFromConditionsOrVariables" access="public" output="false" hint="Analyzes values in condition or variable configurations and parses out variables scopes.">
		<cfscript>
			var local = {};
			local.lsFoundScopes = '';
			if (hasConditions()) { local.items = getConditions(); }
			else if (hasVariables()) { local.items = getVariables(); }
		</cfscript>
		
		<cfloop from="1" to="#ArrayLen(local.items)#" index="i">
			<cfset local.sParsedScope = ListGetAt(GetToken(local.items[i][1],1),1,'.') />
			<cfif ListFindNoCase(local.lsFoundScopes,local.sParsedScope) EQ 0>
				<cfset local.lsFoundScopes = ListAppend(local.lsFoundScopes,local.sParsedScope) />
			</cfif>
		</cfloop>
		
		<cfreturn local.lsFoundScopes />
	</cffunction>
	
	
	<!--- buildDynamicKey() --->
	<cffunction name="buildDynamicKey" access="public" output="false" returntype="void" hint="Loops through provided key-value pairs and appends them to the static cache key to generate an instance-specific cache key.  It also limits the key to 250 characters.">
		<cfargument name="arKeyPairs" type="array" required="yes" />
		<cfif (hasVariables())>
			<cfset local.sGeneratedKey = getCacheKey() />
			<cfloop from="1" to="#ArrayLen(Arguments.arKeyPairs)#" index="i">
				<cfset local.sGeneratedKey = local.sGeneratedKey & "_" & arguments.arKeyPairs[i][1] & ":" & arguments.arKeyPairs[i][2] />
			</cfloop>
			<cfset local.sGeneratedKey = Left(local.sGeneratedKey,250) />
			<cfset setCacheKey(local.sGeneratedKey) />
		</cfif>
	</cffunction>
	
	
	<!--- retrieveCacheData() --->
	<cffunction name="retrieveCacheData" access="public" output="false" returntype="void" hint="Queries the cache engine for the object's cache key. Updates self status on result.">
		<cfset local.CacheData = getCacheEngine().get(getCacheKey()) />
		<cfif (local.CacheData EQ "")>
			<cfset setFoundInCache(false) />
		<cfelse>
			<cfset setCacheData(local.CacheData) />
			<cfset setFoundInCache(true) />
		</cfif>
	</cffunction>
	
	
	<!--- saveCacheData() --->
	<cffunction name="saveCacheData" access="public" output="false" returntype="void" hint="Takes data to cache as argument and saves that data to the caching engine.  It also fires off related methods that generate debug markup and write static files.">
		<cfargument name="cacheData" type="any" required="true" />
		<cfset var local = StructNew() />
		
		<!--- Localize the cache data to the method --->
		<cfset local.cacheDataToSave = arguments.cacheData />
		
		<!--- Check for debug mode and if needed generate debug output --->
		<cfif (variables.instance["oFusecacheFactory"].inDebugMode())>
			<cfset local.cacheDataToSave = generateDebugMarkup(local.cacheDataToSave) />
		</cfif>
		
		<!--- Run the cache engine's 'set' command --->
		<cfset local.result = getCacheEngine().set(key='#getCacheKey()#',value='#local.CacheDataToSave#',expiry='#getExpiration()#') />
		
		<!--- Check for static file write mode and call method if necessary --->
		<cfif (variables.instance["oFusecacheFactory"].doWriteStaticFiles() AND (getStaticFilePath() NEQ ""))>
			<cfset writeStaticFile(local.cacheDataToSave) />
		</cfif>
		
	</cffunction>
	
	
	<!--- generateDebugMarkup() --->
	<cffunction name="generateDebugMarkup" access="public" output="false" returntype="string" hint="Wraps cache data with markers to help show caching activity.">
		<cfargument name="cacheData" type="any" required="true" />
		<cfset var local = StructNew() />
		<cfset local.sDebugMarkup = "<!-- START CACHED DATA -->" & arguments.cacheData & "<!-- END CACHED DATA -->" />
		<cfreturn local.sDebugMarkup />
	</cffunction>
	
	
	<!--- writeStaticFile() --->
	<cffunction name="writeStaticFile" access="private" output="false" returntype="void" hint="Based on this FusecacheItem's settings, this method will output the related cache data to a static file.">
		<cfargument name="cacheData" type="any" required="true" />
		<cfset var local = {} />
		<cfset local.sFilePath = getStaticFileRootPath() & getStaticFilePath() />
		<cfif (NOT DirectoryExists(GetDirectoryFromPath(ExpandPath(local.sFilePath))))>
			<cfdirectory action="create" directory="#GetDirectoryFromPath(ExpandPath(local.sFilePath))#" />
		</cfif>
		<cfset FileWrite(ExpandPath(local.sFilePath),arguments.cacheData) />
		<cfset variables.instance["oFusecacheFactory"].getStaticFileManager().addFileRecord(local.sFilePath,getExpiration()) />
	</cffunction>

</cfcomponent>